package com.example.ass7.sprite;

import java.awt.Image;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;

import biuoop.DrawSurface;

import com.example.ass7.collision.HitListener;
import com.example.ass7.dataloader.Background;
import com.example.ass7.geometryprimitives.Point;
import com.example.ass7.level.GameLevel;

/**
 * @author Meirav Ben Izhak
 */
public class Formation implements Sprite {

    private static final String BACKGROUND_PATH = "block_images/alien.png";
    private List<Block> aliens;
    private String direction = "RIGHT";
    private double speed = 50;
    private double pixelGap = 15;
    private double downPixel = 15;
    private Point topLeft = new Point(50, 50);
    private GameLevel game;

    /**
     * constructor.
     */
    public Formation() {
        try {
            aliens = setAliens();
        } catch (IOException ioe) {
            System.out.println(ioe.getMessage());
        }
    }

    /**
     * @param speed1 speed
     */
    public void setSpeed(double speed1) {
        this.speed = speed1;
    }

    /**
     * @return List<Block>
     * @throws IOException IOException
     */
    private List<Block> setAliens() throws IOException {
        List<Block> tempAliens = new ArrayList<Block>();
        double width = 40;
        double height = 30;
        InputStream inputStream = ClassLoader.getSystemClassLoader()
                .getResourceAsStream(BACKGROUND_PATH);
        Image image = ImageIO.read(inputStream);
        Background background = new Background(image, null);

        for (int row = 0; row < 5; row++) {
            for (int column = 0; column < 10; column++) {
                Block alien = new Block(
                        new Point(topLeft.getX() + (width + pixelGap) * column,
                                topLeft.getY() + (height + pixelGap) * row),
                        width, height, background);
                alien.setAlien(Boolean.TRUE);
                tempAliens.add(alien);
            }
        }
        return tempAliens;
    }

    /**
     * @param alien alien
     */
    public void removeAlien(Block alien) {
        this.aliens.remove(alien);
    }

    /**
     * @param dt dt
     */
    public void move(double dt) {
        if (direction.equals("LEFT")) {
            if (overLeftBorder()) {
                // move down
                moveDown();
                // change direction
                direction = "RIGHT";
                increaseSpeed();
            } else {
                // run left
                moveLeft(dt);
            }
        } else if (direction.equals("RIGHT")) {
            if (overRightBorder()) {
                // move down
                moveDown();
                // change direction
                direction = "LEFT";
                increaseSpeed();
            } else {
                // run right
                moveRight(dt);
            }
        }
    }

    /**
     * @param hl hl
     */
    public void addHitListener(HitListener hl) {
        for (Block alien : aliens) {
            alien.addHitListener(hl);
        }
    }

    /**
     * void.
     */
    private void increaseSpeed() {
        speed = speed * 1.1;
    }

    /**
     * void.
     */
    private void moveDown() {
        for (Block alien : aliens) {
            Point point = alien.getPosition();
            alien.setPosition(
                    new Point(point.getX(), point.getY() + downPixel));
        }
        if (getYHighest() >= 500) {
            game.decreaseLive(1);
            game.stop();
        }
    }

    /**
     * @return double
     */
    private double getYHighest() {
        double highest = 0;
        for (Block alien : aliens) {
            if (alien.getCollisionRectangle().getyHighest() > highest) {
                highest = alien.getCollisionRectangle().getyHighest();
            }
        }
        return highest;
    }

    /**
     * @return double
     */
    private double getYLowest() {
        double lowest = 800;
        for (Block alien : aliens) {
            if (alien.getCollisionRectangle().getyLowest() < lowest) {
                lowest = alien.getCollisionRectangle().getyLowest();
            }
        }
        return lowest;
    }

    /**
     * @return double
     */
    private double getXLowest() {
        double lowest = 800;
        for (Block alien : aliens) {
            if (alien.getCollisionRectangle().getxLowest() < lowest) {
                lowest = alien.getCollisionRectangle().getxLowest();
            }
        }
        return lowest;
    }

    /**
     * @param dt dt
     */
    private void moveLeft(double dt) {
        for (Block alien : aliens) {
            Point point = alien.getPosition();
            alien.setPosition(
                    new Point(point.getX() - (speed * dt), point.getY()));
        }
    }

    /**
     * @param dt dt
     */
    private void moveRight(double dt) {
        for (Block alien : aliens) {
            Point point = alien.getPosition();
            alien.setPosition(
                    new Point(point.getX() + (speed * dt), point.getY()));
        }
    }

    /**
     * @return boolean
     */
    private boolean overRightBorder() {
        for (Block alien : aliens) {
            if (alien.overRightBorder()) {
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }

    /**
     * @return boolean
     */
    private boolean overLeftBorder() {
        for (Block alien : aliens) {
            if (alien.overLeftBorder()) {
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }

    /**
     * @param addTo addTo
     */
    public void addToGame(GameLevel addTo) {
        game = addTo;
        addTo.addSprite(this);
        for (Block alien : aliens) {
            addTo.addSprite(alien);
            addTo.addCollidable(alien);
        }
    }

    /**
     * @return int
     */
    public int getRemainingAlienSize() {
        return this.aliens.size();
    }

    @Override
    public void drawOn(DrawSurface d) {
        // do nothing
    }

    @Override
    public void timePassed(double dt) {
        this.move(dt);
    }

    /**
     * @return Block
     */
    public Block getRandomAlienShootOut() {
        java.util.Random rand = new java.util.Random();
        Map<Double, Block> map = new HashMap<Double, Block>();

        for (Block alien : aliens) {
            Block lowestAlien = map.get(alien.getPosition().getX());
            if (lowestAlien == null) {
                map.put(alien.getPosition().getX(), alien);
            } else if (lowestAlien.getPosition().getY()
                    < alien.getPosition().getY()) {
                map.put(alien.getPosition().getX(), alien);
            }
        }
        if (map.size() == 0) {
            return null;
        }
        int index = rand.nextInt(map.size());
        return (Block) map.values().toArray()[index];
    }

    // set position on top left
    /**
     * @param alienSpeed alienSpeed
     */
    public void begin(double alienSpeed) {
        speed = alienSpeed;

        double backX = getXLowest() - topLeft.getX();
        double backY = getYLowest() - topLeft.getY();

        for (Block alien : aliens) {
            alien.setPosition(
                    new Point(alien.getPosition().getX() - backX,
                            alien.getPosition().getY() - backY));
        }
    }

}
